﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.ComponentModel;

// Modified based on code in this article: http://www.gbogea.com/2010/01/02/mvvm-multiselect-listbox
// Written by:  Gabriel Perez 

namespace OpenLightGroup.Common
{
    
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SelectionCollection<T> :
      ObservableCollection<SelectableItem<T>> where T : IComparable<T>
    {
        #region Properties

        /// <summary>
        /// Returns the selected items in the list
        /// </summary>
        public IEnumerable<T> SelectedItems
        {
            get { return this.Where(x => x.IsSelected).Select(x => x.Item); }
        }

        /// <summary>
        /// Returns all the items in the SelectionList
        /// </summary>
        public IEnumerable<T> AllItems
        {
            get { return this.Select(x => x.Item); }
        }

        #endregion

        #region ctor

        /// <summary>
        /// Creates a new instance of the selection collection, that wraps an IEnumerable
        /// </summary>
        /// <param name="collection">Collection to wrap</param>
        public SelectionCollection(IEnumerable<T> collection)
            : base(toSelectionItemEnumerable(collection))
        {
            if (collection == null)
                throw new ArgumentNullException("collection", "Collection is null.");

            this.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectionCollection_CollectionChanged);

            foreach (var item in this.Items)
                item.SelectionChanged += new EventHandler(SelectionItem_SelectionChanged);

        }


        #endregion

        #region Public methods

        /// <summary>
        /// Adds the item to the list
        /// </summary>
        /// <param name="item">Item to add</param>
        public void Add(T item)
        {
           this.Add(false, item);

        }

        /// <summary>
        /// Adds the item to the list
        /// </summary>
        /// <param name="item">Item to add</param>
        /// <param name="isSelected">Is the item selected</param>
        public void Add(bool isSelected, T item)
        {
           
            SelectableItem<T> selectableItem = new SelectableItem<T>(isSelected, item);

            base.Add(selectableItem);

            selectableItem.SelectionChanged += new EventHandler(SelectionItem_SelectionChanged);


        }

        /// <summary>
        /// Removes an item from the collection
        /// </summary>
        /// <param name="item">Item to remove</param>
        public void Remove(T item)
        {
            if (this.Contains(item))
            {

                foreach (SelectableItem<T> i in this.Items)
                {

                    if (item.CompareTo(i.Item) == 0)
                    {
                        i.SelectionChanged -= new EventHandler(SelectionItem_SelectionChanged);

                        base.Remove(i);

                        break;

                    }

                }

            }

        }

        /// <summary>
        /// Checks if the item exists in the list
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Contains(T item)
        {
            return AllItems.Contains(item);
        }

        /// <summary>
        /// Selects all the items in the list
        /// </summary>
        public void SelectAll()
        {
            foreach (SelectableItem<T> selectionItem in this)
            {
                selectionItem.IsSelected = true;
            }
        }

        /// <summary>
        /// Unselects all the items in the list
        /// </summary>
        public void UnselectAll()
        {
            foreach (SelectableItem<T> selectionItem in this)
            {
                selectionItem.IsSelected = false;
            }
        }

        #endregion

        #region Event Handlers

        void SelectionCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.OnPropertyChanged(new PropertyChangedEventArgs("SelectedItems"));
            this.OnPropertyChanged(new PropertyChangedEventArgs("AllItems"));

        }


        void SelectionItem_SelectionChanged(object sender, EventArgs e)
        {

            this.OnPropertyChanged(new PropertyChangedEventArgs("SelectedItems"));

        }

        #endregion

        #region Helper methods

        /// <summary>
        /// Creates an SelectionList from any IEnumerable
        /// </summary>
        /// <param name="items"></param>
        /// <returns></returns>
        private static IEnumerable<SelectableItem<T>> toSelectionItemEnumerable(IEnumerable<T> items)
        {
            List<SelectableItem<T>> list = new List<SelectableItem<T>>();
            foreach (T item in items)
            {
                SelectableItem<T> selectionItem = new SelectableItem<T>(item);

                list.Add(selectionItem);

            }
            return list;
        }



        #endregion


    }

}
